﻿namespace SubhadraSolutions.Sharp.Utils.Reflection.Emit
{
    using System;
    using System.Reflection.Emit;

    using SubhadraSolutions.Sharp.Utils.Reflection.Internal;

    public static class EmitHelper
    {
        /// <summary>
        /// Replaces the array element.
        /// </summary>
        /// <param name="ilGen">The il gen.</param>
        /// <param name="codeToPlaceArrayOnStack">The code to place array on stack.</param>
        /// <param name="index">The index.</param>
        /// <param name="codeToPlaceElementOnStack">The code to place element on stack.</param>
        public static void ReplaceArrayElement(this ILGenerator ilGen, OpCode codeToPlaceArrayOnStack, int index, OpCode codeToPlaceElementOnStack)
        {
            ReplaceArrayElement(ilGen, codeToPlaceArrayOnStack, index, () => ilGen.Emit(codeToPlaceElementOnStack));
        }

        /// <summary>
        /// Replaces the array element.
        /// </summary>
        /// <param name="ilGen">The il gen.</param>
        /// <param name="codeToPlaceArrayOnStack">The code to place array on stack.</param>
        /// <param name="index">The index.</param>
        /// <param name="actionToPlaceElementOnStack">The action to place element on stack.</param>
        public static void ReplaceArrayElement(this ILGenerator ilGen, OpCode codeToPlaceArrayOnStack, int index, Action actionToPlaceElementOnStack)
        {
            ilGen.Emit(codeToPlaceArrayOnStack);
            ilGen.Emit(OpCodes.Ldc_I4, index);
            actionToPlaceElementOnStack();
            ilGen.Emit(OpCodes.Stelem_Ref);
        }

        /// <summary>
        /// Creates the array.
        /// </summary>
        /// <param name="ilGen">The il gen.</param>
        /// <param name="arrayType">Type of the array.</param>
        /// <param name="size">The size.</param>
        /// <param name="localVariableIndexToSave">The local variable index to save.</param>
        public static void CreateArray(this ILGenerator ilGen, Type arrayType, int size, int localVariableIndexToSave)
        {
            ilGen.Emit(OpCodes.Ldc_I4, size);
            ilGen.Emit(OpCodes.Newarr, arrayType);
            ilGen.Emit(OpCodes.Stloc, localVariableIndexToSave);
        }

        /// <summary>
        /// Calls the safe invoke.
        /// </summary>
        /// <param name="ilGen">The il gen.</param>
        /// <param name="codeToPlaceDelegateOnStack">The code to place delegate on stack.</param>
        /// <param name="codeToPlaceArgumentArrayOnStack">The code to place argument array on stack.</param>
        public static void CallSafeInvoke(this ILGenerator ilGen, OpCode codeToPlaceDelegateOnStack, OpCode codeToPlaceArgumentArrayOnStack)
        {
            ilGen.Emit(codeToPlaceDelegateOnStack);
            ilGen.Emit(codeToPlaceArgumentArrayOnStack);
            ilGen.Emit(OpCodes.Call, SharedReflectionInfo.SafeInvokeMethod);
        }
    }
}
